package org.vacoor.xqq.ui.comp.tree;

import javax.swing.*;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author: Vacoor
 */
public class AnimatedTree extends JTree {
    private ShakePlayer shakePlayer;

    public AnimatedTree(TreeModel model) {
        super(model);
        getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        setToggleClickCount(1);
        setRootVisible(false);
    }

    /**
     * 根据 id 查找
     */
    public IdentifiableAndObservableDataNode getIdentifiableAndObservableNode(long id) {
        TreeModel model = getModel();
        if (model instanceof DataNodeMonitorTreeModel) {
            return ((DataNodeMonitorTreeModel) model).getIdentifiableAndObservableNode(id);
        }
        return getIdentifiableAndObservableNode((DefaultMutableTreeNode) model.getRoot(), id);
    }

    private IdentifiableAndObservableDataNode getIdentifiableAndObservableNode(DefaultMutableTreeNode parent, long id) {
        // 这里使用广度优先搜索
        Enumeration<TreeNode> nodes = parent.breadthFirstEnumeration();
        while (nodes.hasMoreElements()) {
            TreeNode node = nodes.nextElement();
            if (!(node instanceof IdentifiableAndObservableDataNode)) {
                continue;
            }
            IdentifiableAndObservableDataNode identifiableNode = (IdentifiableAndObservableDataNode) node;
            if (identifiableNode.getId() == id) {
                return identifiableNode;
            }
        }
        return null;
    }

    public void setShaking(long id, boolean shaking) {
        IdentifiableAndObservableDataNode n = getIdentifiableAndObservableNode(id);
        if (n == null || !(n instanceof ShakeableNode<?>)) {
            return;
        }
        if (shaking) {
            ensurePlayer();
            shakePlayer.addShake((ShakeableNode) n);
        } else {
            if (shakePlayer != null) {
                shakePlayer.removeShake(id);
            }
        }
    }

    /**
     * 重绘给定的 row 所对应节点
     */
    public void repaint(int row) {
        // 如果 row 不存在返回null
        repaint(this.getPathForRow(row));
    }

    /**
     * 重绘 给定 TreePath 对应节点
     */
    public void repaint(TreePath path) {
        // 如果 path == null || path 不存在, 则返回null
        Rectangle bounds = this.getPathBounds(path);
        if (bounds == null) {
            return;
        }
        this.repaint(bounds);
    }

    /**
     * 重绘给定节点
     *
     * @param treeNode
     */
    public void repaint(final TreeNode treeNode) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) treeNode;
                TreePath path = new TreePath(node.getPath());
                Rectangle bounds = getPathBounds(path);
                int row = getRowForPath(path);
                // 可见
                if (-1 != row && bounds != null) {
                    repaint(bounds);
                }
            }
        });
    }


    private void ensurePlayer() {
        if (shakePlayer != null) {
            return;
        }
        synchronized (AnimatedTree.class) {
            if (shakePlayer == null) {
                shakePlayer = new ShakePlayer();
            }
        }
    }

    // Shake 管理器
    protected class ShakePlayer implements ActionListener {
        private Timer timer = new Timer(200, this);
        private Map<Long, ShakeableNode> shakeList = new ConcurrentHashMap<Long, ShakeableNode>();

        public void addShake(ShakeableNode node) {
            if (node == null) {
                return;
            }
            shakeList.put(node.getId(), node);
            node.setShaking(true);

            // 当跳动列表有内容时启动
            if (shakeList.size() > 0) {
                timer.start();
            }
        }

        public void removeShake(long id) {
            ShakeableNode node = shakeList.remove(id);
            if (node == null) {
                return;
            }
            node.setShaking(false);     // 停止当前节点跳动
            repaint(node);              // 重绘当前节点
            TreeNode p = node.getParent();
            if (p instanceof ShakeableNode) {
                ((ShakeableNode) p).setShaking(false);  // 停止父节点跳动
                repaint(p);
            }

            // 当列表没有元素时停止
            if (shakeList.size() < 1) {
                timer.stop();
            }
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            for (ShakeableNode n : shakeList.values()) {
                repaint(n);
                TreeNode parent = n.getParent();
                if (parent == null || !(parent instanceof ShakeableNode)) {
                    continue;
                }
                ShakeableNode p = (ShakeableNode) parent;
                // 如果父节点没有展开, 则设置父节点跳动
                if (!isExpanded(new TreePath(p.getPath()))) {
                    p.setShaking(true);
                    repaint(p);
                } else if (p.isShaking()) { // 展开 但仍是 shaking, 则取消
                    p.setShaking(false);
                    repaint(p);
                }
            }
        }
    }
}
